package erfgame.core.graphics.renderer.chain;

import java.util.ArrayList;
import java.util.Collection;

import erfgame.core.graphics.renderer.EntityRenderer;
import erfgame.core.graphics.renderer.RendererContext;
import erfgame.core.graphics.renderer.RendererUpdateListener;
import erfgame.core.graphics.renderer.RendererUpdateType;

public class RendererChain implements RendererUpdateListener {
	private EntityRenderer head;
	
	private ArrayList<RendererContext> contexts;
	
	public RendererChain( EntityRenderer head ) {
		this.head = head;
		this.contexts = new ArrayList<RendererContext>(1);
	}
	
	public EntityRenderer getHead() {
		return this.head;
	}
	
	public ArrayList<RendererContext> getContexts() {
		return this.contexts;
	}
	
	public void add( ChainedRenderer renderer ) {
		boolean started = isStarted();
		EntityRenderer current = head;
		ChainedRenderer previous = null;
		int rendererPriority = renderer.getPriority();
		
		while( (current instanceof ChainedRenderer) && ((ChainedRenderer)current).getPriority() > rendererPriority ) {
			previous = (ChainedRenderer)current;
			current = previous.getChild();
		}
		renderer.setChild( current, started );
		if( previous != null ) {
			previous.setChild(renderer, started);
		} else {
			this.head = renderer;
			if( started ) {
				this.head.setUpdateListener(this);
			}
		}
		if( started ) {
			renderer.start();
		}
	}
	
	public void remove( int type ) {
		boolean started = isStarted();
		EntityRenderer current = head;
		ChainedRenderer previous = null;
		
		// TODO this could be useful
		//int rendererPriority = renderer.getPriority();
		
		while( current.getType() != type ) {
			if( current instanceof ChainedRenderer ) {
				previous = (ChainedRenderer)current;
				current = previous.getChild();
			} else {
				return;
			}
		}
		current.stop();
		current.setUpdateListener(null);
		EntityRenderer child = ((ChainedRenderer)current).getChild();
		((ChainedRenderer)current).setChild(null, false);
		if( previous != null ) {
			previous.setChild( child, started );
		} else {
			this.head = child;
			if( started ) {
				this.head.setUpdateListener(this);
			}
		}
	}
	
	public boolean isAttachedTo( RendererContext context ) {
		return this.contexts.contains( context );
	}
	
	public void attach( Collection<RendererContext> contexts ) {
		boolean empty = this.contexts.isEmpty();
		this.contexts.addAll( contexts );
		if( empty ) {
			start();
		}
	}
	
	public void attach( RendererContext context ) {
		boolean empty = this.contexts.isEmpty();
		this.contexts.add( context );
		if( empty ) {
			start();
		}
	}
	
	public void detach( RendererContext context ) {
		if( this.contexts.remove( context ) ) {
			if( this.contexts.isEmpty() ) {
				stop();
			}
		}
	}
	
	private void start() {
		EntityRenderer current = this.head;
		current.setUpdateListener(this);
		while( current != null ) {
			current.start();
			if( current instanceof ChainedRenderer ) {
				current = ((ChainedRenderer)current).getChild();
			} else {
				current = null;
			}
		}
	}

	private void stop() {
		EntityRenderer current = this.head;
		current.setUpdateListener(null);
		while( current != null ) {
			current.stop();
			if( current instanceof ChainedRenderer ) {
				current = ((ChainedRenderer)current).getChild();
			} else {
				current = null;
			}
		}
	}
	
	private boolean isStarted() {
		return this.contexts.size() > 0;
	}

	public void rendererUpdated(RendererUpdateType type, Object state) {
		// TODO may want to repaint different areas if the size has changed!
		repaint();
	}
	
	protected void repaint() {
		for( int i=contexts.size(); i>0; ) {
			i--;
			RendererContext context = contexts.get( i );
			context.requestRepaint(head);
		}
	}
}
